import time
import random
import traceback

from mysql import Mysql
import jwt
import sanic
from sanic_ext import Extend
from sanic import Sanic, HTTPResponse
from sanic.response import text, json

app = Sanic("lottery")
app.config.CORS_ORIGINS = "*"
Extend(app)


@app.get('/')
async def index(request):
    return text('hello')


@app.post('/login')
async def login(request: sanic.Request):
    req = request.json
    if req is None:
        return json({'code': -1, 'msg': '请输入手机号与密码'})
    mobile = req.get('mobile')
    password = req.get('password')
    if mobile is None or password is None:
        return json({'code': -1, 'msg': '请输入手机号与密码'})
    with Mysql() as db:
        user = db.select_one('select * from `user` where mobile=%s and password=%s', (mobile, password))
        if user is None:
            return json({'code': -1, 'msg': '用户不存在或密码错误'})
        uid = user['id']
        award = db.select('select * from points_log where user_id=%s and `type`=1000', uid)
    token = jwt.encode({"jti": uid, "iat": int(time.time())}, "secret", algorithm="HS256")
    if isinstance(token, bytes):
        token = str(token, encoding="utf-8")
    result = {'code': 200, 'token': token, 'award': False}
    if award is None or len(award) == 0:
        result['award'] = True
    return json(result)


@app.get('/user')
async def get_user(request: sanic.Request):
    uid, err = check_user(request)
    if err is not None:
        return err
    with Mysql() as db:
        user = db.select_one('select nickname,points from `user` where id=%s', uid)
    if user is None:
        return json({'code': 401, 'msg': '该用户无效'})
    result = {'code': 200}
    result.update(user)
    return json(result)


@app.get('/award')
async def get_award(request: sanic.Request):
    uid, err = check_user(request)
    if err is not None:
        return err
    req = request.args
    _type = req.get('type')
    if _type is None:
        return json({'code': -1, 'msg': '需要选择一个类型'})
    _type = int(_type)
    with Mysql() as db:
        award = db.select('select * from points_log where user_id=%s and `type`=1000', uid)
    if award is not None and len(award) != 0:
        return json({'code': -1, 'msg': '已领取该奖励，无法再次领取'})
    return text('hello')


@app.get('/myAward')
async def get_my_ward(request: sanic.Request):
    uid, err = check_user(request)
    if err is not None:
        return err
    req = request.args
    _type = req.get('type')
    if _type is None:
        return json({'code': -1, 'msg': '需要选择一个类型'})
    _type = int(_type) + 100
    with Mysql() as db:
        awards = db.select("select info, DATE_FORMAT(created_time,'%%Y-%%m-%%d %%H:%%i:%%s') time "
                           "from points_log where user_id=%s and `type`=%s order by created_time desc limit 10"
                           , (uid, _type))
    return json({'code': 200, 'awards': awards})


@app.get('/lottery')
async def get_lottery(request: sanic.Request):
    req = request.args
    _type = req.get('type')
    if _type is None:
        return json({'code': -1, 'msg': '需要选择一个类型'})
    with Mysql() as db:
        lottery = db.select_one('select * from lottery where id=%s', _type)
    if lottery is None:
        return json({'code': -1, 'msg': '该抽奖已结束'})
    prizes: str = lottery['prizes'].split(',')
    _range = lottery['range'].split(',')
    for i, r in enumerate(_range):
        _range[i] = float(r)
    return json({'code': 200, 'prizes': prizes, 'range': _range})


@app.post('/lottery')
async def post_lottery(request: sanic.Request):
    req = request.args
    _type = req.get('type')
    if _type is None:
        return json({'code': -1, 'msg': '需要选择一个类型'})
    _type = int(_type)
    uid, err = check_user(request)
    if err is not None:
        return err
    with Mysql() as db:
        # noinspection PyBroadException
        try:
            points, err = add_points_to_user(db, uid, -50, 100, '积分抽奖：' + str(_type))
            if err is not None:
                db.rollback()
                return err
            lottery = db.select_one('select `range`, award from lottery where id=%s', _type)
            if lottery is None:
                db.rollback()
                return json({'code': -1, 'msg': '该抽奖已结束'})
            weights = lottery['range']
            awards = lottery['award']
            user_weights = db.select_one('select `range` from user_range where user_id=%s and lottery_id=%s',
                                         (uid, _type))
            if user_weights is not None:
                weights = user_weights['range']
            
            weights = weights.split(',')
            awards = awards.split(',')
            population = [i for i, w in enumerate(weights)]
            for i, r in enumerate(weights):
                weights[i] = float(r)
            award_index = random.choices(population, weights=weights)[0]
            award = int(awards[award_index])
            if award > 0:
                _, err = add_points_to_user(db, uid, award, 101, '抽中：' + str(award) + '积分')
                if err is not None:
                    db.rollback()
                    return err
            return json({'code': 200, 'index': award_index, 'points': points})
        except Exception:
            traceback.print_exc()
            db.rollback()
            return json({'code': -1, 'msg': '发生错误'})
    

def add_points_to_user(db: Mysql, uid, amount, _type, reason) -> (int, HTTPResponse):
    user = db.select_one('select * from `user` where id=%s', uid)
    if user is None:
        return None, json({'code': 401, 'msg': '该用户无效'})
    points = user['points']
    new_points = points + amount
    if amount < 0:
        if points + amount < 0:
            return None, json({'code': -1, 'msg': '用户积分不足'})
    db.execute_not_commit('update `user` set points=points + %s where id=%s', (amount, uid))
    db.execute_not_commit('insert into points_log(user_id, `type`, points, info) values (%s, %s, %s, %s)',
                          (uid, _type, amount, reason))
    return new_points, None
    

def check_user(request: sanic.Request) -> (int, HTTPResponse):
    headers = request.headers
    token = headers.get('Token')
    if token is None:
        return None, json({'code': 401, 'msg': '请先登录'})
    try:
        token = jwt.decode(token, "secret", algorithms=["HS256"])
    except jwt.exceptions.DecodeError:
        return None, json({'code': 401, 'msg': '登录信息有误，请重新登录'})
    return token['jti'], None


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=53882, access_log=False)
